/**
 * J<i>ava</i> U<i>tilities</i> for S<i>tudents</i>
 */
package jus.aor.mobilagent.kernel;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;

import java.util.List;
import java.util.jar.JarException;
import java.util.logging.Level;
import java.util.logging.Logger;
import jus.aor.mobilagent.loader.BAMLoader;
import jus.aor.mobilagent.route.Etape;
import jus.aor.mobilagent.route.EtapeFinale;
import jus.aor.mobilagent.route.EtapeInitiale;
import jus.aor.mobilagent.route.Route;

/**
 * Le serveur principal permettant le lancement d'un serveur d'agents mobiles et
 * les fonctions permettant de déployer des services et des agents.
 *
 * @author Morat
 */
public final class Server implements ServerInterface {

    /**
     * le nom logique du serveurBAMLoader
     */
    protected String name;
    /**
     * le port où sera ataché le service du bus à agent mobile. Pafr défaut on
     * prendra le port 10140
     */
    protected int port = 10140;
    /**
     * le server d'agent démarré sur ce noeud
     */
    protected AgentServer agentServer;
    /**
     * le nom du logger
     */
    protected String loggerName;
    /**
     * le logger de ce serveur
     */
    protected Logger logger = null;
    protected BAMLoader loader;

    /**
     * Démarre un serveur de type mobilagent
     *
     * @param port le port d'écuote du serveur d'agent
     * @param name le nom du serveur
     */
    public Server(final int port, final String name, BAMLoader loader) {
        this.name = name;
        this.loader = loader;
        try {
            this.port = port;
            /* mise en place du logger pour tracer l'application */
            loggerName = "jus/aor/mobilagent/" + InetAddress.getLocalHost().getHostName() + "/" + this.name;
            logger = Logger.getLogger(loggerName);
            /* démarrage du server d'agents mobiles attaché à cette machine */
            //A COMPLETER
            agentServer = new AgentServer(port, name, loader);
            agentServer.start();
            /* temporisation de mise en place du server d'agents */
            //    Thread.sleep(1000);
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.log(Level.FINE, " erreur durant le lancement du serveur" + this, ex);
            return;
        }
    }

    /**
     * Ajoute le service caractérisé par les arguments
     *
     * @param name nom du service
     * @param classeName classe du service
     * @param codeBase codebase du service
     * @param args arguments de construction du service
     */
    @Override
    public final void addService(String name, String classeName, String codeBase, Object... args) {
        try {
            loader.addJar(openJar(codeBase));

            Class<? extends ServiceInterface> c = (Class<? extends ServiceInterface>) Class.forName(classeName);
            ServiceInterface s = c.getConstructor(Object[].class).newInstance(args);
            agentServer.addService(name, s);
        } catch (Exception ex) {
            logger.log(Level.FINE, " erreur durant le lancement du serveur" + this, ex);
            return;
        }
    }

    /**
     * deploie l'agent caractérisé par les arguments sur le serveur
     *
     * @param classeName classe du service
     * @param args arguments de construction de l'agent
     * @param codeBase codebase du service
     * @param etapeAddress la liste des adresse des étapes
     * @param etapeAction la liste ds actions des étapes
     */
    @Override
    public final void deployAgent(String classeName, Object[] args, String codeBase, List<Pair<String>> routeInfo) {
        try {
            final BAMLoader agentLoader = new BAMLoader(loader);
            Jar j = openJar(codeBase);
            agentLoader.addJar(j);

            Route route = new Route(new EtapeInitiale(agentServer), new EtapeFinale(agentServer));
            for (Pair<String> p : routeInfo) {
//                System.out.println("Route add " + p.first + " : " + p.second);
                route.addEtape(new Etape(new URI(p.first), p.second));
            }

            Class<AgentInterface> ac = (Class<AgentInterface>) agentLoader.loadClass(classeName);

            final AgentInterface agent = ac.getConstructor(Route.class).newInstance(route);
            agent.setClasses(j);

            agentServer.startAgent(agent, agentLoader);
        } catch (Exception ex) {
            ex.printStackTrace();
            logger.log(Level.FINE, " erreur durant le lancement du serveur" + this, ex);
            return;
        }
    }

    private Jar openJar(String codeBase) throws JarException, MalformedURLException, IOException {
        return new Jar(System.getProperty("user.dir") + new URL(codeBase).getPath());
    }
}
